import { EventSender } from "../flash/EventSender";
import { IIOHandler } from "../../Framework/Util/IIOHandler";
import { IBusinessHandler } from "../../Framework/Util/IBusinessHandler";
import { IProtocol } from "../../Framework/Protocol/IProtocol";
import { Protocol } from "../../Framework/Protocol/CDFProtocol/Protocol";
import { AMI_IKeepActive_keepActive } from "./AMI_IKeepActive_keepActive";
import { Session } from "./Session";
import { RMIException } from "./RMIException";
import { ArrayHolder } from "../../Framework/Holder/ArrayHolder";
import { RMIObjectBind } from "./RMIObjectBind";
import { SerializeStream } from "../../Framework/Serialize/SerializeStream";
import { ERMIMessageType } from "./ERMIMessageType";
import { SRMICall } from "./SRMICall";
import { RMIObject } from "./RMIObject";
import { Context } from "./Context";
import { ERMIDispatchStatus } from "./ERMIDispatchStatus";
import { SRMIReturn } from "./SRMIReturn";
import { MessageBlock } from "../../Framework/MQ/MessageBlock";
import { Exception } from "../../Framework/Util/Exception";
import ByteArray = require("bytearray-node");
import { Socket } from "net";
import { Timer, getTimer } from "../../flash/utils";
import { Event, DataEvent } from "../../flash/events";
type int = number;
//class RMIConnection

export class RMIConnection extends EventSender
  implements IIOHandler, IBusinessHandler {
  constructor(arg1: IProtocol) {
    
    super();if (null != arg1) {
      this._protocol = arg1;
    } else {
      this._protocol = new Protocol();
    }
    this._socket = new Socket /* flash.net.Socket */();
    this._socket.addListener("error", this.ioErrorHandler.bind(this));
    this._socket.addListener("connect", this.connectHandler.bind(this));
    this._socket.addListener("close", this.closeHandler.bind(this));
    // this._socket.addListener("error", this.securityErrorHandler);
    this._socket.addListener("data", this.socketDataHandler.bind(this));
    this._timer = new Timer(/* flash.utils.Timer */ this._delay);
    this._timer.addEventListener("timer", this.onTimerHandler.bind(this));
    this._timeOutCount = 0;
    this._keepActionBack = new AMI_IKeepActive_keepActive();
  }

  private onTimerHandler(arg1: Event /* flash.events.TimerEvent */): void {
    if (
      this._session &&
      this._session.communicator &&
      this._session.communicator.connected
    ) {
      this._timeOutCount++;
      if (this._timeOutCount === 2) {
        this._session.keepActivePrx.keepActive_async(this._keepActionBack);
      }
    }
    return;
  }

  public get session(): Session {
    return this._session;
  }

  public set session(arg1: Session) {
    this._session = arg1;
    return;
  }

  private ioErrorHandler(arg1: Error /* flash.events.IOErrorEvent */): void {
    console.error(arg1.toString());
    var message = arg1.message;
    this.close();
    if (this._session.communicator.sessionEvent) {
      this._session.communicator.sessionEvent.onAbandon(this._session);
    }
    this.processException(message, RMIException.ExceptionCodeConnectionWrite);
  }

  private connectHandler(arg1: Object /* flash.events.Event */): void {
    this._timeOutCount = 0;
    this._timer.start();
    this.dispatchEvent(new Event("connect"));
    this._session.connection = this;
    this._session.communicator.connecting = false;
    this._session.communicator.connected = true;
    var arrayHolder = new ArrayHolder<RMIObjectBind>();
    this._session.pinkMessage(arrayHolder);
    var i = 0;
    while (i < arrayHolder.value.length) {
      let value = arrayHolder.value[i];
      if (value.outgoingStatus == RMIObjectBind.EOutGoingWaitToSend) {
        if (-1 == this.send(value.buffer.byteArray)) {
          if (value.rmiObject) {
            this._session.removeBackObject(value.messageId);
            let loc4 = new RMIException(
              "RMIConnection: send fail",
              RMIException.ExceptionCodeConnectionWrite
            );
            value.rmiObject.cdeException(loc4);
          }
        }
      }
      ++i;
    }
  }

  private closeHandler(arg1: Object /* flash.events.Event */): void {
    this.dispatchEvent(new Event("close"));
    console.error("RMIConnection closeHandler");
    this.close();
    if (this._session.communicator.sessionEvent) {
      this._session.communicator.sessionEvent.onAbandon(this._session);
    }
    this.processException(
      "Connected Close",
      RMIException.ExceptionCodeConnectionClosed
    );
    this._protocol.clear();
    return;
  }

  private securityErrorHandler(
    arg1: Error /* flash.events.SecurityErrorEvent */
  ): void {
    console.error("RMIConnection:" + arg1.message);
    var loc1 = this._session.communicator.url + ":" + arg1.message;
    // @ts-ignore
    this._session.connection = null;
    this._session.communicator.connecting = false;
    this._session.communicator.connected = false;
    if (this._session.communicator.sessionEvent) {
      this._session.communicator.sessionEvent.onAbandon(this._session);
    }
    this.processException(loc1, RMIException.ExceptionCodeConnectionNotConnect);
    return;
  }

  private socketDataHandler(
    buf: Buffer /* flash.events.ProgressEvent */
  ): void {
    var start_time = getTimer();
    var loc2 = new ByteArray(buf);
    loc2.endian = "LE";
    if (-1 == this._protocol.handleData(loc2, this, this)) {
      throw new Error("网络数据处理失败");
    }
    var diff = getTimer() - start_time;
    this.dispatchEvent(
      new DataEvent(
        /* flash.events.DataEvent */ "网络数据处理时间",
        false,
        true,
        diff.toString()
      )
    );
  }

  public connect(host: string, port: int): void {
    this._session.communicator.connecting = true;
    this._socket.connect({
      host,
      port
    });
    return;
  }

  public close(): void {
    this._socket.destroy();
    this._timer.removeEventListener("timer", this.onTimerHandler.bind(this));
    this._timer.stop();
    // @ts-ignore
    this._session.connection = null;
    this._session.communicator.connecting = false;
    this._session.communicator.connected = false;
    this._timeOutCount = 0;
    return;
  }

  public send(arg1: ByteArray): int {
    return this._protocol.sendDataEncrypt(arg1, this) ? 0 : -1;
  }

  public sendData(inBuf: ByteArray): int {
    try {
      this._timeOutCount = 0;
      this._socket.write(inBuf.buffer);
      // this._socket.end();
    } catch (ex) {
      return -1;
    }
    return 0;
  }

  public handlePacket(inBuf: ByteArray, handler: IBusinessHandler): Boolean {
    var isStream: SerializeStream = new SerializeStream();
    isStream.byteArray = inBuf;
    var messageType: ERMIMessageType = ERMIMessageType.__read(isStream);
    var call: SRMICall;
    var context: Context;
    var outStream: SerializeStream;
    var dispatchStatus: ERMIDispatchStatus;
    var __os: SerializeStream;
    var callRet: SRMIReturn;
    var callRet1: SRMIReturn;
    var rmiObjectBind: RMIObjectBind;
    var context1: Context;
    var mb: MessageBlock;

    if (messageType.equals(ERMIMessageType.MessageTypeCall)) {
      call = new SRMICall();
      call.__read(isStream);
      var rmiObject = this._session.findObject(call.identity);
      if (null == rmiObject) {
        console.error(call.identity.name + " object not find");
        return true;
      }
      context = new Context();
      context._session = this._session;
      context._connection = this;
      context._messageId = call.messageId;
      context._dispachStatus = ERMIDispatchStatus.DispatchOK;
      outStream = new SerializeStream();
      try {
        dispatchStatus = rmiObject.__dispatch(
          context,
          call,
          isStream,
          outStream
        );
      } catch (ex) {
        console.error(ex.message);
        outStream.clear();
        outStream.writeException(ex);
        dispatchStatus = ERMIDispatchStatus.DispatchException;
      }
      if (
        call.messageId == 0 ||
        dispatchStatus.equals(ERMIDispatchStatus.DispatchAsync)
      ) {
        return true;
      }
      __os = new SerializeStream();
      ERMIMessageType.MessageTypeCallRet.__write(__os);
      callRet = new SRMIReturn();
      callRet.messageId = call.messageId;
      callRet.dispatchStatus = dispatchStatus;
      callRet.__write(__os);
      __os.writeByteArray(outStream);
      return !(this.send(__os.byteArray) == -1);
    }
    if (messageType.equals(ERMIMessageType.MessageTypeCallRet)) {
      callRet1 = new SRMIReturn();
      callRet1.__read(isStream);
      rmiObjectBind = this._session.findRemoveBackObject(callRet1.messageId);
      if (rmiObjectBind == null) {
        console.error(callRet1.messageId + " message id not find");
        return true;
      }
      context1 = new Context();
      context1._session = this._session;
      context1._connection = this;
      context1._messageId = callRet1.messageId;
      context1._dispachStatus = callRet1.dispatchStatus;
      rmiObjectBind.__back(context1, isStream);
    } else if (messageType.equals(ERMIMessageType.MessageTypeMQ)) {
      mb = new MessageBlock();
      mb.__read(isStream);
      if (this._session.messageHandler) {
        this._session.messageHandler.onMessage(mb);
      }
    }
    return true;
  }

  private processException(arg1: string, arg2: int): void {
    var loc4 = null;
    var loc1 = new ArrayHolder();
    this._session.pinkRemoveMessage(loc1);
    var loc2 = new Exception(arg1, arg2);
    var loc3 = 0;
    while (loc3 < loc1.value.length) {
      if ((loc4 = loc1.value[loc3] as RMIObjectBind) != null) {
        if (this._session.communicator.prepareCommandHandler) {
          this._session.communicator.prepareCommandHandler.prepareBackException(
            loc4.rmiObject,
            loc2
          );
        }
        loc4.rmiObject.cdeException(loc2);
      }
      ++loc3;
    }
    return;
  }

  private _socket: Socket /* flash.net.Socket */;

  private _protocol: IProtocol;

  private _session!: Session;

  private _timeOutCount: int;

  private _timer: Timer /* flash.utils.Timer */;

  private _delay: int = 10000;

  private _keepActionBack: AMI_IKeepActive_keepActive;
}
